/*
 * BlockSorting.cpp
 *
 *  Created on: 27/05/2014
 *      Author: manuel
 */

#include "BlockSorting.h"

BlockSorting::BlockSorting(unsigned short tam_bloque, unsigned short optimiz) :
	tamano_bloque(tam_bloque), optimizacion(optimiz){

	nombre_archivo = "";
	subfijo_cod = ".cod.BS";
	subfijo_decod = ".decod.BS";
}

BlockSorting::~BlockSorting() {}

void decod(unsigned char* F, unsigned char* L, unsigned int* O, unsigned short I, unsigned int index, unsigned int N)
// funcion recursiva que genera el string original apartir de L, P y I
{
	if( index == N )
		return;

	decod( F, L, O, O[I], index+1, N );

	F[index] = L[ O[I] ];
}

void BlockSorting::decodificarBloque(unsigned char *L, unsigned short I, unsigned char *F, unsigned int N)
// ejemplo:
// L = string salida del block-sorting = lbsdzaaaaan
// I = indice salida del block sorting = 9
// O = vector con los caracteres de L ordenados = aaaaabdlnsz
// P = vector con las posiciones de los caracteres de C en L = 5 6 7 8 8 9 1 3 0 10 2 4
{
	unsigned char* O;
	unsigned int P[N];

	O = new unsigned char [N];

	memcpy(O, L, N);

	std::sort(O, O+N);

	unsigned int i, j=0;
	for( i=0 ; i<N ; i++ ){
		for( ; O[i]!=L[j] ; j++)
			;
		P[i]=j;
		if( O[i] == O[i+1] )
			j++;
		else
			j=0;
	}

	decod(F, L, P, I, 0, N);

	delete[] O;
}

void rotaciones(unsigned char *s, unsigned char **m, int n)
// cada posicion 'i' del vector M es un puntero al i-esimo caracter del bloque a codificar
{
    int i=0;

    m[0] = s;
    for (i=1; i<n; ++i)
        m[i] = &s[i];

    return;
}

void BlockSorting::codificarBloque
	(unsigned char *String,int N, unsigned char *L, unsigned short &I, unsigned short n)
{

    unsigned char **M; // matriz de rotaciones de S
    M = new unsigned char*[N];

    unsigned char* S;
    S = new unsigned char[N+n+2]; // vector con los N primeros caracteres agregados al final

    memcpy(S, String, N);

    for (unsigned short k=0; k<n; k++){
    	S[N+k] = S[k];
    }

    if ( n != 0) {
    	S[N+n] = 255; //  	le asignamos a los ultimos 2 bytes el valor de 255,
    	S[N+n+1] = 255; //	que representan al EOF. seria el caracter mas alto
    }
    rotaciones(S, M,N); //realizamos las rotaciones de la matriz

    unsigned short i=0;

    i=0;
    quick_sort(M,0,N-1,N+n+2); // ordenamos las rotaciones
    for (i=0; M[i]!=S; ++i)
    	//std::cout << "M[" << i << "] " << M[i] << std::endl;
    	;

    I = i; // I = index

    for (i=0; i<N; ++i){
        L[i] = *(M[i]-1);
        //si es el 1er caracter, entonces no busca el caracter anterior, sino q busca el ultimo caracter
        if ( i == I )
        	L[i] = *(M[i]+N-1);
    }

    delete []M;
    delete []S;
    return;
}

std::string BlockSorting::codificarArchivo(const char* nombre_arch) {

	Bloque bloqueArchivo(tamano_bloque); // bloque del archivo original/codificado
	Bloque bloqueCodif(tamano_bloque); // bloque codificado

	unsigned short idx;
	Bloque bloqueIndex( sizeof(idx) ); // para guardar los indexs de cada bloque

	this->nombre_archivo = nombre_arch;

	std::string arch(nombre_archivo);
	std::string codif(nombre_archivo); codif += subfijo_cod;

	Archivo archivo(arch); archivo.abrir();
	Archivo codificado(codif); codificado.abrir();

	std::cout << "CODIFICANDO ARCHIVO...\n";
	unsigned short tam_bloque;
	while( archivo.leerBloque(bloqueArchivo) ){

		tam_bloque = archivo.bytesLeidos();
		codificarBloque(bloqueArchivo.getData(), tam_bloque, bloqueCodif.getData(), idx, optimizacion);

		memcpy(bloqueIndex.getData(), (const unsigned char*) &idx, 2);

		codificado.escribirBloque(bloqueCodif);
		codificado.escribirBloque(bloqueIndex);

		bloqueArchivo.iniciar();
		bloqueCodif.iniciar();
		bloqueIndex.iniciar();
	}

	tam_bloque = archivo.bytesLeidos();

	Bloque bloqueFinal(tam_bloque);

	/// procesamos los ultimos bytes que no llegan a completar 1 bloque ////////////

	codificarBloque(bloqueArchivo.getData(), tam_bloque, bloqueCodif.getData(), idx, optimizacion);

	memcpy(bloqueIndex.getData(), (const unsigned char*) &idx, 2);
	memcpy(bloqueFinal.getData(), bloqueCodif.getData(), tam_bloque);

	codificado.escribirBloque(bloqueFinal);
	codificado.escribirBloque(bloqueIndex);

	bloqueArchivo.iniciar();
	bloqueCodif.iniciar();
	bloqueIndex.iniciar();
	///////////////////////////////////////////////////////////////////////////////

	archivo.cerrar();
	codificado.cerrar();

	return nombre_archivo+subfijo_cod;
}

void BlockSorting::decodificarArchivo(const char* nombre_arch_codif) {

	std::string nombre_cod(nombre_arch_codif);

	std::string nombre_decod = nombre_cod.substr(0, nombre_cod.size()-subfijo_cod.size());
	nombre_decod += subfijo_decod;

	Archivo codificado(nombre_cod); codificado.abrir();
	Archivo decodificado(nombre_decod); decodificado.abrir();

	Bloque bloqueCodif(tamano_bloque); // bloque codificado
	Bloque bloqueDecodif(tamano_bloque); // bloque decodificado

	unsigned short idx;
	Bloque bloqueIndex( sizeof(idx) ); // para guardar los indexs de cada bloque

	std::cout << "DECODIFICANDO ARCHIVO...\n";
	unsigned int tam_bloque;
	while( ( codificado.leerBloque(bloqueCodif) && (tam_bloque = codificado.bytesLeidos()) )
			&& codificado.leerBloque(bloqueIndex) ){

		memcpy( (unsigned char*) &idx, bloqueIndex.getData(), 2);

		decodificarBloque(bloqueCodif.getData(), idx, bloqueDecodif.getData(), tam_bloque);
		decodificado.escribirBloque(bloqueDecodif);

		bloqueCodif.iniciar();
		bloqueDecodif.iniciar();
		bloqueIndex.iniciar();
	}
	/// procesamos los ultimos bytes que no llegan a completar 1 bloque ////////////
	tam_bloque = codificado.bytesLeidos()-2;

	Bloque bloqueFinal(tam_bloque);

	memcpy( (unsigned char*)&idx, &bloqueCodif.getData()[bloqueFinal.getTamano()], 2);

	bloqueFinal.iniciar();
	bloqueDecodif.iniciar();

	decodificarBloque(bloqueCodif.getData(), idx, bloqueDecodif.getData(), bloqueFinal.getTamano());

	memcpy(bloqueFinal.getData(), bloqueDecodif.getData(), bloqueFinal.getTamano());

	decodificado.escribirBloque(bloqueFinal);

	///////////////////////////////////////////////////////////////////////////////

	codificado.cerrar();
	decodificado.cerrar();
}

